from helpers import updateInfo
from helpers import Visualization
from helpers import CompHelp
from helpers import Transactions
from helpers import Insights
import time
import math
import datetime
import pickle
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.api import mail

class MainPage(webapp.RequestHandler):
    """ Daily cron job class """
    def get(self):
        updateAll()

def computeCronWeeklyRate(currList, index):
    """ Compute the weekly rate change in stock for seasonality purposes """
    totalAmount=0.0
    position=0
    
    if currList<=7:
        return 1
        
    while(position < 7):
        totalAmount+= currList[index-position]
        position+=1
        
    if totalAmount<=0:
        return 1
        
    return totalAmount
                
def runUpdateStock():
        """ Download data from I4S of a all stocks (chuncks of 10 at a time) """    
        
        q_1 = db.GqlQuery("SELECT * FROM StocksData WHERE cronupdatedate<:1 LIMIT 0,10", datetime.date.today())
        # If finished updating for today
        if q_1.count()==0:
            return 1

        for p in q_1:
            try:
                oInsights = Insights.Insights(p.stockname)
                #pastdate=p.buydate.strftime("%Y-%m-%d")
                # If stock data is available from Google Insight
                
                if oInsights.printLastDate()<>"":
                    
                    #prevLastDateValue=float(oInsights.section('trend')[oInsights.printprevLastDate()])
                    # If last updated date value was 0, then change to 1
                    #if prevLastDateValue==0:
                        #prevLastDateValue=1
                        
                    #tempPrcnt=round(float(oInsights.section('trend')[oInsights.printLastDate()])/prevLastDateValue,2)
                    
                    #computing precentChange for the last day, based on weekly average
                    p.last30Days=oInsights.getLast30Days()
                    length=len(p.last30Days)
                    todayRate=computeCronWeeklyRate(oInsights.getLast30Days(), length-1)
                    yestradayRate=computeCronWeeklyRate(oInsights.getLast30Days(), length-2)
                    tempPrcnt=round(todayRate/yestradayRate,4)
                    
                    p.updatedate=datetime.datetime.fromtimestamp(time.mktime(time.strptime(oInsights.printLastDate(),"%Y-%m-%d")))    

                    p.precentChange=tempPrcnt
                    p.topRegions=oInsights.getTopRegionsList()
                    p.relatedSearches=oInsights.getRelatedSearches()
                    #p.MonthChange=oInsights.get30DaysChange()
                    #p.ThreeMonthChange=oInsights.get90DaysChange()
                    p.isProfile=True
                else:
                    p.precentChange=1.0
                    
            except:
                # Error downloading data (do not change the percentage of the stock)
                p.precentChange=1.0
                
            p.cronupdatedate=datetime.date.today()
            p.put()


        return 0
        
def runUpdateETF():
    """ Update ETF values """
    total=0.0
    q_1 = db.GqlQuery("SELECT * FROM ETFData WHERE cronupdatedate<:1 LIMIT 0,10", datetime.date.today())
    # If finished updating for today
    if q_1.count()==0:
        return 1
        
    for etfRow in q_1:
        total=0.0
        flag=0

        for stock in etfRow.stocklist:
        
            current_stock=db.get(stock)
            total=total+current_stock.precentChange

            if current_stock.updatedate==None:
                continue
            else:
                if flag==0:
                    minDate=current_stock.updatedate
                    flag=1
                else:
                    if current_stock.updatedate<minDate:
                        minDate=current_stock.updatedate
        if flag==1:
            etfRow.updatedate=minDate
        
        etfRow.precentChange=round(total/len(etfRow.stocklist),4)
        etfRow.cronupdatedate=datetime.date.today()
        etfRow.isProfile=True
        etfRow.put()

    return 0
    
def computeWeeklyRate(list,index,totalPrevIteration) :
    """ Seasonality update computation """
    if totalPrevIteration==0:
        totalPrevIteration=computeCronWeeklyRate(list, index)
        return totalPrevIteration , float(totalPrevIteration/7)
    else:    
        total= totalPrevIteration-list[index-7]+list[index]
    return total,float(total/7)
    
def adjustListValues(list,length):
    """ Seasonality update computation func """
    index=6
    totalPrevIteration=0.0
    newList=[]
    
    while (index<length):
        totalPrevIteration,ret=computeWeeklyRate(list,index,totalPrevIteration)
        newList.append(round(ret,2))
        index+=1
    return newList
        
        
def runUpdateSeasonality():
    """ Update all stocks graphs with seasonality adjustment """
    
    q_1 = db.GqlQuery("SELECT * FROM StocksData WHERE seasonalityUpdateDate<:1 LIMIT 0,3", datetime.date.today())
    #If finished updating for today
    if q_1.count()==0:
        return 1
    for stock in q_1:
        list=stock.last30Days
        length=len(list)
        if length >= 36 :
            adjustedList=adjustListValues(list,length)
            stock.last30Days=adjustedList
            stock.MonthChange=round(stock.last30Days[len(stock.last30Days)-1]/stock.last30Days[0],2)
            stock.FifteenDaysChange=round(stock.last30Days[len(stock.last30Days)-1]/stock.last30Days[len(stock.last30Days)-15],2)
            
        stock.seasonalityUpdateDate=datetime.date.today()
        stock.put()
    return 0
                    
def runUpdateUsers():
        """ Update all users investment portfolio according to changes in stocks values """
        
        q_1 = db.GqlQuery("SELECT * FROM TrendUsers WHERE cronupdatedate<:1 LIMIT 0,2", datetime.date.today())
        # If finished updating for today
        if q_1.count()==0:
            return 1

        for trenduser in q_1:
            historyuser=updateInfo.getCurrentUserGraph(trenduser.tuser)

            accumamount=0.0
            prevAccumamount=0.0
            
            q_2 = db.GqlQuery("SELECT * FROM Stocks WHERE stockref!=NULL AND buyer=:1", trenduser.tuser)
            for stock in q_2:
                # Downloading data from google succeeded and date is up-to-date
                prevAccumamount+=stock.currValue
                if (not stock.stockref.updatedate==None and stock.buydate<stock.stockref.updatedate):
#                    stock.currValue=stock.currValue-+round(stock.currValueBase*stock.stockref.precentChange,2)
                    stock.currValue*=round(((stock.currValue/stock.currValueBase)+stock.stockref.precentChange)/2,2)
                    stock.userPrecentChange=round(stock.currValue/stock.buyValue,4)
                    stock.userupdatedate=stock.stockref.updatedate
                    stock.currValueBase=stock.currValue
                #Update stock data
                stock.put()
                    # Accumulate sum update
                accumamount+=stock.currValue
                
            q_3 = db.GqlQuery("SELECT * FROM Stocks WHERE basketref!=NULL AND buyer=:1 ", trenduser.tuser)
            
            for stock in q_3:
            #Downloading data from google succeeded and date is up-to-date
                prevAccumamount+=stock.currValue
                
                if (not stock.basketref.updatedate==None and stock.buydate<stock.basketref.updatedate):
                    stock.currValue=round(stock.currValue*stock.basketref.precentChange,2)
                    stock.userPrecentChange=round(stock.currValue/stock.buyValue,4)
                    stock.userupdatedate=stock.basketref.updatedate
                        
                #Update stock data
                stock.put()
                    
                # Accumulate sum update
                accumamount+=stock.currValue
        
            # Update user amount from investments
            trenduser.totalamount=accumamount+trenduser.bankamount

            if prevAccumamount<=0:
                trenduser.userPrecentChangeDaily=1.0
            else:
                trenduser.userPrecentChangeDaily=round(accumamount/prevAccumamount,4)
            if trenduser.alert>0 and math.fabs(1-trenduser.userPrecentChangeDaily)>=trenduser.alert:
                PrintPercentage, _, _ = updateInfo.getPerInfo(trenduser.userPrecentChangeDaily)            
                mail.send_mail(sender="TrendSE <trendse.team@gmail.com>",
                                    to="<" + trenduser.tuser.email() + ">",
                                    subject="TrendSE Portfolio Report",
                                    body="Hi!\n\nFollowing your request, we would like to notify you that your portfolio has changed by %%%.2f during the last 24 hours.\nPlease click on the link below to view full details:\nhttp://www.mytrendse3.appspot.com/report\n\nGood Luck!\nTrendSE Team" % (PrintPercentage))
            # Make sure only 30 days are saved
            if len(historyuser.history)>=Visualization.graphHistoryDays():
                historyuser.history.pop(0)
            
            historyuser.history.append(round(trenduser.totalamount,2))
            
            historyuser.put()
            trenduser.cronupdatedate=datetime.date.today()
            trenduser.put()
        return 0    
        
def runUpdateCompetitions():
    """ Update all competitions (status, etc.) """
    
    q_3 = db.GqlQuery("SELECT * FROM Competition")
    for competition in q_3:

        startToday=True
        # Competition has not started yet
        if competition.status==0:
            # Competition should start today
            if competition.startdate<=datetime.date.today():
                # In case competition has less than 1 user, close it and refund money
                if len(competition.members)<=1:
                    for member in competition.members:
                        trenduser=updateInfo.TrendUsers.get(member)
                        trenduser.totalamount=trenduser.totalamount+competition.compamount
                        trenduser.bankamount=trenduser.bankamount+competition.compamount
                        trenduser.put()
                    competition.status=3
                # In case competition has more than 1 user, open it
                else:
                    competition.status=1
                    if competition.type==1:
                        startToday=True
                    else:
                        startToday=False
                competition.put()
        # Competition is running        
        if competition.status==1 and startToday:


            # In case of portfolio competition
            if competition.type==0:
                # Update % chg data for each user
                i=0
                for member in competition.members:
                    trenduser=updateInfo.TrendUsers.get(member)
                    competition.membersAmount[i]=competition.membersAmount[i]*trenduser.userPrecentChangeDaily
                    i+=1
            elif competition.type==1:
                # Update stock chg data for each stock
                
                
                keywordsList=pickle.loads(competition.arenaKeywords)
                i=0
                for currentKeywords in keywordsList:
                    entitySum=0.0
                    for keyword in currentKeywords:
                        entitySum+=db.get(keyword).precentChange
                    entityAvg=round(entitySum/len(currentKeywords),2)
                    
                    competition.compKeywordsAmount[i]=competition.compKeywordsAmount[i]*entityAvg
                    i+=1

            else:
                # Error type given
                return
            
            # Competition is finished
            if competition.enddate<=datetime.date.today():
                competition.status=2
                winnersList=[]
                                
                # In case of portfolio competition
                if competition.type==0:
                    
                    # Update results list and save maxAmount
                    maxAmount=max(competition.membersAmount)
                    
                    #Check for winner/s
                    i=0
                    for amount in competition.membersAmount:
                        if amount==maxAmount:
                            winnersList.append(competition.members[i])
                        i=i+1
                
                elif competition.type==1:
                
                    sortedIndexList=[[]]
                    sortedIndexList =CompHelp.sortIndex(competition.compKeywordsAmount)
                    winnersList=[]
                    winnersList=CompHelp.sortWinners(sortedIndexList,competition.members,competition.membersChoice) 
                
                # Win amount (divide total bank by the number of winners)
                if len(winnersList)==0:
                    winAmount=0
                else:
                    if not competition.isAd:
                        winAmount=round(float(((competition.compamount)*(len(competition.members)))/len(winnersList)),2)
                    elif competition.isAd:
                        winAmount=round(float(competition.advamount)/len(winnersList),2)
                    # Should never get here! 
                    else:
                        winAmount=0
                
                # Update winners total amount
                for winner in winnersList:
                    win_user=updateInfo.TrendUsers.get(winner)
                    win_user.totalamount=win_user.totalamount+winAmount
                    win_user.bankamount=win_user.bankamount+winAmount
                    win_user.put()
                    
            competition.put()
        
        # Competition has finished
        elif competition.status==2 or competition.status==3:
            # Delete Competition after two weeks
            if (datetime.date.today()-competition.enddate).days>=14:
                # Remove competition from competitions list of all members
                
                # Remove stocks if is in arena
                if competition.type==1:
                    keywordsList=pickle.loads(competition.arenaKeywords)
                    for currentKeywords in keywordsList:
                        for keyword in currentKeywords:
                            # delete stock data if needed
                            try:
                                currentKeyword=db.get(keyword)
                                if currentKeyword:
                                    currentKeyword.arenacounter-=1
                                    currentKeyword.put()
                                    categoryRef=currentKeyword.categoryref
                                    Transactions.deleteFromStockDb(currentKeyword)        
                                    # delete category data if needed
                                    Transactions.deleteFromStockCategoryDb(categoryRef)    
                            except:
                                continue
                for comp_users in competition.members:
                    trenduser=updateInfo.TrendUsers.get(comp_users)
                    trenduser.complist.remove(competition.key())
                    trenduser.put()
                # Remove the competition itself
                competition.delete()
                
        # Invalid tuple
        else:
            continue

    # Currently all competitions run at the same time
    return 1    

def updateAll():
        """ Begin cron job - check if cron job needs to run """
        # Select cron job last finished time
        q_1 = db.GqlQuery("SELECT * FROM CronJ Limit 0,1")
        # Run only if today cron job is not finished
        result=q_1.fetch(1)
        if result[0].rundate<datetime.date.today():
        
            if result[0].phase==0:    
                if runUpdateStock()==1:
                    result[0].phase=1
                    result[0].put()
                    
            elif result[0].phase==1:
                if runUpdateETF()==1:
                    result[0].phase=2
                    result[0].put()
                    
            elif result[0].phase==2:
                if runUpdateUsers()==1:
                    result[0].phase=3
                    result[0].put()    
                    
            # overcomong seasonality effect by weekly price
            elif result[0].phase==3:
                if runUpdateSeasonality()==1:
                    result[0].phase=4
                    result[0].put()    

            # Currently all competitions are run at one time (no cronjob updatedate)
            elif result[0].phase==4:
                if runUpdateCompetitions()==1:
                    result[0].phase=5
                    result[0].put()    
                    
            else:
                result[0].rundate=datetime.date.today()
                result[0].phase=0
                #Update last cron job finished job for today
                result[0].put()
            
application = webapp.WSGIApplication(
                                     [('/crondir/cronjob', MainPage)],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()